package com.rectcircle.wechat.api;

import java.util.List;

import com.rectcircle.wechat.msg.vo.Message;
import com.rectcircle.wechat.msg.vo.Music;
import com.rectcircle.wechat.msg.vo.NewsItem;
import com.rectcircle.wechat.msg.vo.Video;

/**
 * 
 * 关于消息的相关api的封装（普通事件消息、客服消息、群发消息）<br>
 * 包括以下功能（具体使用见示例）
 * <ul>
 * <li>根据通过XMLHandler.parseXML(...)
 * 解析XML格式的普通（事件）消息生成的Message
 * 和其他参数，生成回复的Message对象：
 * 这一组方法的命名形式为：replyXxxMsg(...) //Xxx为回复的消息类型，
 * 
 * <li>根据参数生成客服消息的对象KfMessage，
 * 这一组方法的命名形式为：createXxxKfMsg(...) //Xxx为回复消息的类型
 * <li>根据KfMessage对象发送客服消息
 * <li>根据参数生成群发消息的对象MassMessage，
 * 这一组方法的命名形式为：createXxxMassMsg(...) //Xxx为回复消息的类型、
 * <li>根据 MassMessage对象群发消息
 * <li>根据 TmMessage（模板消息）对象群发消息
 * <li>获取自动回复规则
 * </ul>
 * <p>
 * <h2>注意：</h2>
 * <h3>一、关于客服消息</h3>
 * 当用户和公众号产生特定动作的交互时（具体动作列表请见下方说明），微信将会把消息数据推送给开发者，开发者可以在一段时间内（目前修改为48小时）调用客服接口，通过POST一个JSON数据包来发送消息给普通用户。此接口主要用于客服等有人工消息处理环节的功能，方便开发者为用户提供更加优质的服务。
 * <br><br>
 * 目前允许的动作列表如下（公众平台会根据运营情况更新该列表，不同动作触发后，允许的客服接口下发消息条数不同，下发条数达到上限后，会遇到错误返回码，具体请见返回码说明页）：
 * <br>
 * <pre>
 * 1、用户发送信息
 * 2、点击自定义菜单（仅有点击推事件、扫码推事件、扫码推事件且弹出“消息接收中”提示框这3种菜单类型是会触发客服接口的）
 * 3、关注公众号
 * 4、扫描二维码
 * 5、支付成功
 * 6、用户维权
 * </pre>
 * <p>
 * 
 * <h3>二、关于高级群发消息</h3>
 * 在公众平台网站上，为订阅号提供了每天一条的群发权限，为服务号提供每月（自然月）4条的群发权限。而对于某些具备开发能力的公众号运营者，可以通过高级群发接口，实现更灵活的群发能力。
 * <br><br>
 * <h4>请注意：</h4>
 * <br>
 * <pre>
 * 1、对于认证订阅号，群发接口每天可成功调用1次，此次群发可选择发送给全部用户或某个分组；
 * 2、对于认证服务号虽然开发者使用高级群发接口的每日调用限制为100次，但是用户每月只能接收4条，无论在公众平台网站上，还是使用接口群发，用户每月只能接收4条群发消息，多于4条的群发将对该用户发送失败；
 * 3、具备微信支付权限的公众号，在使用群发接口上传、群发图文消息类型时，可使用{@code <a>}标签加入外链；
 * 4、开发者可以使用预览接口校对消息样式和排版，通过预览接口可发送编辑好的消息给指定用户校验效果。
 * </pre>
 * 
 * <h4>群发图文消息的过程如下：</h4>
 * <pre>
 * 1、首先，预先将图文消息中需要用到的图片，使用上传图文消息内图片接口，上传成功并获得图片URL
 * 2、上传图文消息素材，需要用到图片时，请使用上一步获取的图片URL
 * 3、使用对用户分组的群发，或对OpenID列表的群发，将图文消息群发出去
 * 4、在上述过程中，如果需要，还可以预览图文消息、查询群发状态，或删除已群发的消息等
 * </pre>
 * 
 * <h4>群发图片、文本等其他消息类型的过程如下：</h4>
 * <pre>
 * 1、如果是群发文本消息，则直接根据下面的接口说明进行群发即可
 * 2、如果是群发图片、视频等消息，则需要预先通过素材管理接口准备好mediaID
 * </pre>
 * 
 * <h4>关于群发时使用is_to_all为true使其进入公众号在微信客户端的历史消息列表：</h4>
 * <pre>
 * 1、使用is_to_all为true且成功群发，会使得此次群发进入历史消息列表。
 * 2、为防止异常，认证订阅号在一天内，只能使用is_to_all为true进行群发一次，或者在公众平台官网群发（不管本次群发是对全体还是对某个分组）一次。以避免一天内有2条群发进入历史消息列表。
 * 3、类似地，服务号在一个月内，使用is_to_all为true群发的次数，加上公众平台官网群发（不管本次群发是对全体还是对某个分组）的次数，最多只能是4次。
 * 4、设置is_to_all为false时是可以多次群发的，但每个用户只会收到最多4条，且这些群发不会进入历史消息列表。
 * </pre>
 * 
 * <h3>三、关于模板消息</h3>
 * <pre>
 * 1、所有服务号都可以在功能->添加功能插件处看到申请模板消息功能的入口，但只有认证后的服务号才可以申请模板消息的使用权限并获得该权限；
 * 2、需要选择公众账号服务所处的2个行业，每月可更改1次所选行业；
 * 3、在所选择行业的模板库中选用已有的模板进行调用；
 * 4、每个账号可以同时使用25个模板。
 * 5、当前每个账号的模板消息的日调用上限为10万次，单个模板没有特殊限制。【2014年11月18日将接口调用频率从默认的日1万次提升为日10万次，可在MP登录后的开发者中心查看】。当账号粉丝数超过10W/100W/1000W时，模板消息的日调用上限会相应提升，以公众号MP后台开发者中心页面中标明的数字为准。
 * </pre>
 * 
 * <p>
 * 
 * <h2>示例1：发送回复普通消息</h2>
 * <pre>
 * {@code //假设在微信开发者中心填写的Url对应一个Servlet，以下为servlet的post方法}
 * 
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("UTF-8");
		response.setHeader("content-type","text/html;charset=UTF-8");
		response.setCharacterEncoding("UTF-8"); //设置字符编码
		
		Message receiveMsg = XMLHandler.parseXML(request);
		Message replyMsg = MsgApi.replyTextMsg(receiveMsg, "你好"); //在此选择回复的类型内容
		String replyXML = XMLHandler.createXML(replyMsg); //生成XML字符串
		response.getWriter().write(replyXML); //发送回去，到此用户将能收到公众平台的消息
	}
 * </pre>
 * 
 * <p>
 * <h2>示例2：发送客服消息</h2>
 * <pre>
 * {@code //假设在微信开发者中心填写的Url对应一个Servlet，以下为servlet的post方法}

 * protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, ApiRequestErrorException {
		request.setCharacterEncoding("UTF-8");
		response.setHeader("content-type","text/html;charset=UTF-8");
		response.setCharacterEncoding("UTF-8"); //设置字符编码
		String accessToken = ""; //这是您的accesstoken，建议使用缓存这里仅仅是例子
		
		Message receiveMsg = XMLHandler.parseXML(request);
		{@code //...  //发开者根据收到的消息认为需要发送客服消息}
		KfMessage replyMsg = MsgApi.createTextKfMsg(null, receiveMsg.getFromUserName(), "亲，有事么要帮助的？"); //在此选择回复的类型内容
		MsgApi.sendKfMsg(replyMsg, accessToken); //到此用户将能收到公众平台的客服消息
	}
 * 
 * </pre>
 * 
 * <h2>示例3：预览、发送群发消息</h2>
 * <pre>
 * 	String accessToken = ""; //这是您的accesstoken，建议使用缓存这里仅仅是例子
		
	MassMessage msg = MsgApi.createTextMassMsg(true, 1, "这是一个预览测试，请忽略此消息");

	MsgApi.previewMassMsg(msg, accessToken, null, "您需要预览的用户的微信号"); //到此该特定用户将受到这个预览

	MsgApi.sendMassMsg(msg, accessToken); //到此群发消息将发送
 * 
 * </pre>
 * 
 * 
 * <h2>示例4：发送模板消息</h2>
 * <pre>
 * {@code //假设，模板消息id为："_Wp-qUYlA-VHAJjRjxHi_LuqaYIh-mpbg10x4BiWkEI"；内容模板为："您好，您已购买成功。\n\n商品信息：{{name.DATA}}\n{{remark.DATA}}"}
 * 
	String accessToken = ""; //这是你的accessToken

	TmMessage msg = new TmMessage("这是openid",
			"这是模板id，本例应填写：_Wp-qUYlA-VHAJjRjxHi_LuqaYIh-mpbg10x4BiWkEI",
			"这个应填写要跳转的url：例如http://www.qq.com"); //新建一个客服消息
	
	TmParam name = new TmParam("这是商品信息"); //这是一个参数
	TmParam remark = new TmParam("有效期：永久有效\n券号为QQ5024813399，密码为123456890");//这是一个参数
	msg.addParam("name", name);
	msg.addParam("remark", remark); //添加参数
	
	MsgApi.sendTmMsg(msg, accessToken); //到此用户将受到消息
 * </pre>
 * @author RectCircle
 * @version 0.1<br>
 * 2016年7月18日-下午3:20:12<br>
 * Copyright (c) 2016 RectCircle-版权所有<br>
 *
 */
public class MsgApi {
	
	/**
	 * 文本消息
	 */
	public static final String Msg_Type_TEXT = "text";
	
	/**
	 * 图片消息
	 */
	public static final String Msg_Type_IMAGE = "image";
	
	/**
	 * 语音消息
	 */
	public static final String Msg_Type_VOICE = "voice";

	/**
	 * 视频消息
	 */
	public static final String Msg_Type_VIDEO = "video";

	/**
	 * 音乐消息
	 */
	public static final String Msg_Type_MUSIC = "music";

	/**
	 * 图文消息
	 */
	public static final String Msg_Type_NEWS = "news";

	/**
	 * 图文消息
	 */
	public static final String Msg_Type_MPNEWS = "mpnews";

	/**
	 * 卡券消息
	 */
	public static final String Msg_Type_WXCARD = "wxcard";

	/**
	 * 视频消息（群发是用到）
	 */
	public static final String Msg_Type_MPVIDEO = "mpvideo";
	
	/**
	 * 短视频消息
	 */
	public static final String Msg_Type_SHORTVIDEO = "shortvideo";
	
	/**
	 * 地理位置消息
	 */
	public static final String Msg_Type_LOCATION = "location";

	/**
	 * 连接消息（仅在收到接收用户普通消息使用）
	 */
	public static final String Msg_Type_LINK = "link";

	/**
	 * 事件消息
	 */
	public static final String Msg_Type_EVENT = "event";
	
	/**
	 * 点击菜单拉取消息时的事件推送
	 */
	public static final String Event_Type_CLICK = "CLICK";

	/**
	 * 	菜单跳转链接时的事件推送
	 */
	public static final String Event_Type_VIEW = "VIEW";

	/**
	 * 扫码推事件的事件推送
	 */
	public static final String Event_Type_SCANCODE_PUSH = "scancode_push";

	/**
	 * 扫码推事件且弹出“消息接收中”提示框的事件推送
	 */
	public static final String Event_Type_SCANCODE_WAITMSG = "scancode_waitmsg";

	/**
	 * 弹出系统拍照发图的事件推送
	 */
	public static final String Event_Type_PIC_SYSPHOTO = "pic_sysphoto";
	
	/**
	 * 弹出拍照或者相册发图的事件推送
	 */
	public static final String Event_Type_PIC_PHOTO_OR_ALBUM = "pic_photo_or_album";

	/**
	 * 弹出拍照或者相册发图的事件推送
	 */
	public static final String Event_Type_PIC_WEIXIN = "pic_weixin";

	/**
	 * 弹出地理位置选择器的事件推送
	 */
	public static final String Event_Type_LOCATION_SELECT = "location_select";

	/**
	 * 关注事件（或扫描带参数二维码）
	 */
	public static final String Event_Type_SUBSCRIBE = "subscribe";

	/**
	 * 取消关注事件
	 */
	public static final String Event_Type_UNSUBSCRIBE = "unsubscribe";

	/**
	 * 用户已关注并扫描带参数的二维码
	 */
	public static final String Event_Type_SCAN = "SCAN";

	/**
	 * 上报地理位置事件
	 */
	public static final String Event_Type_LOCATION = "LOCATION";

	/**
	 * mass send job finish
	 * 群发消息工作结束时发送的事件
	 */
	public static final String Event_Type_MASSSENDJOBFINISH = "MASSSENDJOBFINISH";
	
	
	/**
	 * template send job finish
	 * 模板消息发送工作结束的事件
	 */
	public static final String Event_Type_TEMPLATESENDJOBFINISH = "TEMPLATESENDJOBFINISH";
	
		
	
	/**
	 * 
	 * 生成文本回复消息对象，可以通过createXML()生成xml格式的文本
	 * 
	 * @param receiveMsg 接收到的消息对象
	 * @param content 回复的文本内容
	 * @return 用于回复的Message对象
	 */
	public static Message replyTextMsg(Message receiveMsg, String content){
		Message msg = setReplyBasicInfo(receiveMsg, Msg_Type_TEXT);
		msg.setContent(content);
		return msg;
	}
	
	/**
	 * 
	 * 生成图片回复消息对象，可以通过createXML()生成xml格式的文本
	 * 
	 * @param receiveMsg 接收到的消息对象
	 * @param mediaId 通过素材管理接口上传多媒体文件，得到的id
	 * @return 用于回复的Message对象
	 */
	public static Message replyImageMsg(Message receiveMsg, String mediaId){
		Message msg = setReplyBasicInfo(receiveMsg, Msg_Type_IMAGE);
		msg.setImage(mediaId);
		return msg;
	}
	
	/**
	 * 
	 * 生成语音回复消息对象，可以通过createXML()生成xml格式的文本
	 * 
	 * @param receiveMsg 接收到的消息对象
	 * @param mediaId 通过素材管理接口上传多媒体文件，得到的id
	 * @return 用于回复的Message对象
	 */
	public static Message replyVoiceMsg(Message receiveMsg, String mediaId){
		Message msg = setReplyBasicInfo(receiveMsg, Msg_Type_VOICE);
		msg.setVoice(mediaId);
		return msg;
	}
	
	/**
	 * 
	 * 生成视频回复消息对象，可以通过createXML()生成xml格式的文本
	 * 
	 * @param receiveMsg 接收到的消息对象
	 * @param mediaId 通过素材管理接口上传多媒体文件，得到的id
	 * @param title 视频消息的标题
	 * @param description 视频消息的描述
	 * @return 用于回复的Message对象
	 */
	public static Message replyVideoMsg(Message receiveMsg, String mediaId, String title, String description){
		Message msg = setReplyBasicInfo(receiveMsg, Msg_Type_VIDEO);
		msg.setVideo(new Video(mediaId, title, description));
		return msg;
	}
	
	/**
	 * 
	 * 生成音乐回复消息对象，可以通过createXML()生成xml格式的文本
	 * 
	 * @param receiveMsg 接收到的消息对象
	 * @param title 音乐标题
	 * @param description 音乐的描述
	 * @param musicUrl 音乐链接
	 * @param hQMusicUrl 高质量音乐链接，WIFI环境优先使用该链接播放音乐
	 * @param thumbMediaId 	缩略图的媒体id，通过素材管理接口上传多媒体文件，得到的id
	 * @return 用于回复的Message对象
	 */
	public static Message replyMusicMsg(Message receiveMsg, String title, String description, String musicUrl, String hQMusicUrl, String thumbMediaId){
		Message msg = setReplyBasicInfo(receiveMsg, Msg_Type_MUSIC);
		msg.setMusic(new Music(title, description, musicUrl, hQMusicUrl, thumbMediaId));
		return msg;
	}
	
	/**
	 * 
	 * 生成图文回复消息对象，可以通过createXML()生成xml格式的文本
	 * 
	 * @param receiveMsg 接收到的消息对象
	 * @param articles 图文消息的List
	 * @return 用于回复的Message对象
	 */
	public static Message replyNewsMsg(Message receiveMsg, List<NewsItem> articles){
		Message msg = setReplyBasicInfo(receiveMsg, Msg_Type_NEWS);
		msg.setArticles(articles);
		msg.setArticleCount(String.valueOf(articles.size()));
		return msg;
	}
	
	/**
	 * 
	 * 根据收到的消息设置返回消息的from和to
	 * 
	 * @param receiveMsg 用户发送给微信号的消息的封装
	 * @param msgType 返回给用户消息的类型
	 * @return 一个消息对象
	 */
	private static Message setReplyBasicInfo(Message receiveMsg, String msgType){
		Message msg = new Message();
		msg.setFromUserName(receiveMsg.getToUserName());
		msg.setToUserName(receiveMsg.getFromUserName());
		msg.setMsgType(msgType);
		msg.setCreateTime(String.valueOf(System.currentTimeMillis()/1000));
		return msg;
	}
	

}
